
#include "Material.h"
#include "Scene.h"
#include "SceneRenderer.h"
#include "Ray.h"

void Materials::white(Ogre::ColourValue& outColour, const FragmentProgramData& fragData, const Material &material, const Scene& scene, const SceneRenderer& sceneRenderer, int recursionLevel)
{
	outColour = Ogre::ColourValue::White;
}

void computePhong(const FragmentProgramData& fragData, const Ogre::Vector3 &eyeVecReflect, const Scene& scene, const SceneRenderer& sceneRenderer, Ogre::ColourValue &ambient, Ogre::ColourValue &diffuse, Ogre::ColourValue &specular)
{
	for (auto i = scene.lights.begin(); i != scene.lights.end(); i++) {
		Ogre::Vector3 lightVec = (*i)->positionViewSpace - fragData.position;
		float distToLight = lightVec.normalise();
		float shadow = sceneRenderer.raycastAny(Ray(fragData.position+lightVec*0.001f, lightVec), scene, distToLight) ? 0.2f : 1.0f;
		float phongDiffuse = std::max(0.0f, lightVec.dotProduct(fragData.normal)) * shadow;
		diffuse += (*i)->diffuse * phongDiffuse;

		float phongSpecular = pow(std::max(0.0f, eyeVecReflect.dotProduct(lightVec)), 100.0f) * shadow;
		specular += (*i)->specular * phongSpecular;
	}
	ambient = Ogre::ColourValue(0.2f, 0.2f, 0.2f);
}

void Materials::phong(Ogre::ColourValue& outColour, const FragmentProgramData& fragData, const Material &material, const Scene& scene, const SceneRenderer& sceneRenderer, int recursionLevel)
{
	Ogre::Vector3 eyeVec = -fragData.position.normalisedCopy();		//we are in view space
	Ogre::Vector3 eyeVecReflect = 2.0f * (fragData.normal.dotProduct(eyeVec) * fragData.normal) - eyeVec;
	Ogre::ColourValue ambient, diffuse, specular;
	computePhong(fragData, eyeVecReflect, scene, sceneRenderer, ambient, diffuse, specular);
	outColour = specular + material.colour * (diffuse + ambient);
}

void Materials::phongReflective(Ogre::ColourValue& outColour, const FragmentProgramData& fragData, const Material &material, const Scene& scene, const SceneRenderer& sceneRenderer, int recursionLevel)
{
	Ogre::Vector3 eyeVec = -fragData.position.normalisedCopy();		//we are in view space
	Ogre::Vector3 eyeVecReflect = 2.0f * (fragData.normal.dotProduct(eyeVec) * fragData.normal) - eyeVec;
	Ogre::ColourValue ambient, diffuse, specular;
	computePhong(fragData, eyeVecReflect, scene, sceneRenderer, ambient, diffuse, specular);
	outColour = material.colour * (diffuse + ambient);
	if (recursionLevel < 2)
	{
		Ogre::ColourValue reflection;
		sceneRenderer.raytrace(reflection, Ray(fragData.position+eyeVecReflect*0.001f, eyeVecReflect), scene, recursionLevel+1);
		outColour = outColour*material.alpha + reflection*(1-material.alpha);
	}
	outColour += specular;
}

void Materials::normals(Ogre::ColourValue& outColour, const FragmentProgramData& fragData, const Material &material, const Scene& scene, const SceneRenderer& sceneRenderer, int recursionLevel)
{
	Ogre::Vector3 n = fragData.normal;
	outColour = Ogre::ColourValue((n.x+1)*0.5f, (n.y+1)*0.5f, (n.z+1)*0.5f, 1.0f);
}

void Materials::barycentricCoords(Ogre::ColourValue& outColour, const FragmentProgramData& fragData, const Material &material, const Scene& scene, const SceneRenderer& sceneRenderer, int recursionLevel)
{
	outColour = Ogre::ColourValue(fragData.userData[0].x, fragData.userData[0].y, fragData.userData[0].z, 1.0f);
}

void Materials::chess(Ogre::ColourValue& outColour, const FragmentProgramData& fragData, const Material &material, const Scene& scene, const SceneRenderer& sceneRenderer, int recursionLevel)
{
	float x = std::abs(fragData.userData[0].x*30 + 1000);
	float y = std::abs(fragData.userData[0].z*30 + 1000);
	if ((int)(x) % 2 == (int)(y) % 2)
		outColour = Ogre::ColourValue::White;

	Ogre::Vector3 eyeVec = -fragData.position.normalisedCopy();		//we are in view space
	Ogre::Vector3 eyeVecReflect = 2.0f * (fragData.normal.dotProduct(eyeVec) * fragData.normal) - eyeVec;
	Ogre::ColourValue ambient, diffuse, specular;
	computePhong(fragData, eyeVecReflect, scene, sceneRenderer, ambient, diffuse, specular);
	outColour = specular + outColour * (diffuse + ambient);
}